#pragma once

#include <iostream>
#include <map>
#include <memory>
#include <queue>
#include <typeindex>

#include <boost/signals2/signal.hpp>
#include <boost/thread.hpp>
#include "data_struct/base/TimeWorkBase/time/SteadyClock.hpp"
#include "modules/AssistModule/LogModule.h"

namespace behavior_controller
{
namespace occur
{
class OccurFailure
{
public:
    OccurFailure();

    ~OccurFailure() { Clear(); }

    template<typename Evt, typename EvtHandler>
    void RegisterEventHandler(
            std::function<void(const std::shared_ptr<Evt> &)> handler,
            time::SteadyClock::Duration timeout = time::SteadyClock::Duration::zero())
    {
        boost::lock_guard<boost::mutex> lock(register_mtx_);
        registered_signals_[std::type_index(typeid(EvtHandler))] =
                Evt::Register([this, handler, timeout](const std::shared_ptr<Evt> &evt_ptr)
                              { PushEventHandler<Evt>(handler, evt_ptr, timeout); });
    }

    template<typename EvtHandler>
    void DeregisterEventHandler()
    {
        boost::lock_guard<boost::mutex> lock(register_mtx_);
        registered_signals_[std::type_index(typeid(EvtHandler))].disconnect();
        registered_signals_.erase(std::type_index(typeid(EvtHandler)));
    }

    void PushHandler(std::function<void()>);

    void Notify();

    void HandleEvents();

    void Clear();

private:
    template<typename Evt>
    void PushEventHandler(
            std::function<void(const std::shared_ptr<Evt> &)> handler,
            const std::shared_ptr<Evt> &evt_ptr, time::SteadyClock::Duration timeout)
    {
        if (timeout == time::SteadyClock::Duration::zero())
        {
            boost::lock_guard<boost::mutex> lock(buffer_mtx_);
            evt_ptr->PreSend();
            receive_buffer_.push([handler, evt_ptr]()
                                 { handler(evt_ptr); });
        } else
        {
            auto create_time = time::SteadyClock::Now();
            boost::lock_guard<boost::mutex> lock(buffer_mtx_);
            evt_ptr->PreSend();
            receive_buffer_.push([handler, evt_ptr, create_time, timeout]()
                                 {
                                     if (time::SteadyClock::Now() - create_time < timeout)
                                     {
                                         handler(evt_ptr);
                                     }
                                 });
        }
        Notify();
    }

    void ProcessEvents();

    bool Wait();

    bool signal_ready_;
    boost::mutex register_mtx_;
    boost::mutex signal_mtx_;
    boost::condition_variable signal_cond_;
    boost::mutex buffer_mtx_;
    std::queue<std::function<void()>> work_buffer_;
    std::queue<std::function<void()>> receive_buffer_;
    std::map<std::type_index, boost::signals2::connection> registered_signals_;
};

} // namespace occur
} // namespace behavior_controller
